﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace GameEngine.PDNParser
{
    /// <summary>
    /// Class for writing PDN file out of syntax tree classes
    /// </summary>
    public class PDNWriter
    {
        private const string keyAsterix = "*";

        private List<string> _lstPDNContent;

        /// <summary>
        /// Construction 
        /// </summary>
        public PDNWriter()
        {
            _lstPDNContent = new List<string>();
        }

        /// <summary>
        /// Returned error message
        /// </summary>
        public string Error
        { get; private set; }

        /// <summary>
        /// Writes the current game to file
        /// </summary>
        public bool WriteGame(Stream s, GameManager _mng)
        {
            PDNRawGameList gameList = new PDNRawGameList();
            PDNFormatter formatter = new PDNFormatter(_mng.GameType);
            PDNRawGame game = formatter.FormatGame(_mng);
            gameList.Games.Add(game);
            return WriteGameList(s, gameList);
        }

        /// <summary>
        /// The main writing method
        /// </summary>
        public bool WriteGameList(Stream s, PDNRawGameList gameList)
        {
            _lstPDNContent.Clear();

            // iterate through all games
            foreach (PDNRawGame game in gameList.Games)
            {
                // write the game
                bool resultWritten;
                WriteGame(game, out resultWritten);

                // if not result written, write asterix sign to terminate the game
                if(! resultWritten)
                {
                    AddLine(keyAsterix);
                }
            }

            // write the file
            try
            {
                StreamWriter writer = new StreamWriter(s);
                foreach(string line in _lstPDNContent.ToArray())
                {
                    writer.WriteLine(line);
                }

                writer.Flush();
            }
            catch (Exception ex)
            {
                Error = ex.Message;
                return false;
            }

            return true;
        }


        /// <summary>
        /// Writes the game to the output
        /// </summary>
        /// <param name="game"></param>
        private void WriteGame(PDNRawGame game, out bool resultWritten)
        {
            resultWritten = false;

            // write PDN tags
            foreach (PDNRawHeader header in game.Headers)
            {
                WritePDNHeader(header);
            }

            // write PDN body
            WriteGameBody(game.GameBody);

            // write the game result only when position is recorded
            if (game.GameBody.Items.Count == 0 && game.GameResult != enumPDNGameResult.None)
            {
                WriteGameResult(game.GameResult);
                resultWritten = true;
            }
        }

        /// <summary>
        /// Writes the game result
        /// </summary>
        private void WriteGameResult(enumPDNGameResult result)
        {
            switch(result)
            {
                case enumPDNGameResult.Win1:
                    AddLine("1-0");
                    break;
                case enumPDNGameResult.Loss1:
                    AddLine("0-1");
                    break;
                case enumPDNGameResult.Draw1:
                    AddLine("1/2-1/2");
                    break;
                case enumPDNGameResult.Win2:
                    AddLine("2-0");
                    break;
                case enumPDNGameResult.Loss2:
                    AddLine("0-2");
                    break;
                case enumPDNGameResult.Draw2:
                    AddLine("1-1");
                    break;
                case enumPDNGameResult.DoubleForfeit:
                    AddLine("0-0");
                    break;
            }            
        }

        /// <summary>
        /// Write the game body
        /// </summary>
        private void WriteGameBody(PDNRawGameBody gameBody)
        {
            string sGameBodyRecord = string.Empty;

            int moveCount = 0;
            
            // iterate through all items
            foreach (PDNRawGameBodyItem item in gameBody.Items)
            {
                string sItem = string.Empty;
                
                // switch item type
                if (item is PDNRawGameBody)
                {
                    AddLine(sGameBodyRecord);
                    sGameBodyRecord = string.Empty;
                    AddLine("(");
                    WriteGameBody(item as PDNRawGameBody);
                    AddLine(")");
                }
                else if (item is PDNRawComment)
                {
                    sGameBodyRecord += " " + GetMoveComment(item as PDNRawComment);
                }
                else if (item is PDNRawNAG)
                {
                    sGameBodyRecord += " " + GetMoveNAG(item as PDNRawNAG);

                }
                else if(item is PDNRawFENSetupString)
                {
                    sGameBodyRecord += " " + GetFENSetupString(item as PDNRawFENSetupString);
                }
                else if (item is PDNRawMove)
                {
                    sGameBodyRecord += " " + GetMove(item as PDNRawMove);
                    moveCount++;
                    if (moveCount % 10 == 0)
                        sGameBodyRecord += "\n";
                }
            } // foreach

            AddLine(sGameBodyRecord);
        }

        /// <summary>
        /// Formats the move for the output to PDN
        /// </summary>
        private string GetMove(PDNRawMove move)
        {
            string sMove = string.Empty;

            if (move.MoveNumber.HasValue)
            {
                sMove += move.MoveNumber.Value + ". ";
            }

            string sMoveSign = move.CaptureMove == true ? "x" : "-";

            if (move.EmptyMove)
            {
                // write empty move
                sMove += " ... ";
            }
            else
            {
                // write the move
                for (int i = 0; i < move.Positions.Count; i++)
                {
                    if (i != 0)
                        sMove += sMoveSign;
                    sMove += move.Positions[i].Position;
                }
            }

            if (string.IsNullOrWhiteSpace(move.MoveStrength) == false)
                sMove += " " + move.MoveStrength;

            return sMove;

        }


        /// <summary>
        /// Returns move comment
        /// </summary>
        private string GetMoveComment(PDNRawComment comment)
        {
            return string.Format("{{ {0} }}", comment.Comment);
        }

        /// <summary>
        /// Returns the nag value
        /// </summary>
        private string GetMoveNAG(PDNRawNAG nag)
        {
            return string.Format("${0}", nag.NagValue);
        }

        /// <summary>
        /// Returns FEN Setup string
        /// </summary>
        private string GetFENSetupString(PDNRawFENSetupString fenString)
        {
            return string.Format("/FEN  \"{0}\"/", fenString.FENSetup);
        }


        /// <summary>
        /// Writes the PDN header
        /// </summary>
        /// <param name="header"></param>
        private void WritePDNHeader(PDNRawHeader header)
        {
            // escape header value quotes
            string value = header.Value.Replace("\"", "\\\"");
            AddLine(string.Format("[{0} \"{1}\"]", header.Key, value));
        }


        /// <summary>
        /// Adds line to the PDN output stream
        /// </summary>
        private void AddLine(string sLine)
        {
            _lstPDNContent.Add(sLine);
        }

    }
}
